{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1Z6Wtb_jisbA"
      },
      "source": [
        "##### Copyright 2019 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "QUyRGn9riopB"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "H1yCdGFW4j_F"
      },
      "source": [
        "# 事前作成された Estimator"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "PS6_yKSoyLAl"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "<img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\"><a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ja/tutorials/estimator/premade.ipynb\">TensorFlow.org で表示</a>\n",
        "</td>\n",
        "  <td>\n",
        "<img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\"><a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ja/tutorials/estimator/premade.ipynb\">Google Colab で実行</a>\n",
        "</td>\n",
        "  <td>\n",
        "<img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\"><a target=\"_blank\" href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ja/tutorials/estimator/premade.ipynb\">GitHub でソースを表示</a>\n",
        "</td>\n",
        "  <td>\n",
        "<img src=\"https://www.tensorflow.org/images/download_logo_32px.png\"><a href=\"https://storage.googleapis.com/tensorflow_docs/docs/site/en/tutorials/estimator/premade.ipynb\">ノートブックをダウンロード</a>\n",
        "</td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "R4YZ_ievcY7p"
      },
      "source": [
        "このチュートリアルでは、Estimator を使用して、TensorFlow でアヤメの分類問題を解決する方法を示します。Estimator は TensorFlow における完全なモデルの高レベルの表現で、スケーリングと非同期トレーニングを行いやすいように設計されています。詳細については、「[Estimators](https://www.tensorflow.org/guide/estimator)」をご覧ください。\n",
        "\n",
        "TensorFlow 2.0 では、[Keras API](https://www.tensorflow.org/guide/keras) を使ってこれらのタスクを達成できます。学習しやすいと考えられている API であるため、新しく始める場合は、Keras から始めることをお勧めします。TensorFlow 2.0 で提供されている高レベル API に関する詳細は、「[Standardizing on Keras](https://medium.com/tensorflow/standardizing-on-keras-guidance-on-high-level-apis-in-tensorflow-2-0-bad2b04c819a)」をご覧ください。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8IFct0yedsTy"
      },
      "source": [
        "## まず最初に\n",
        "\n",
        "始めるには、最初に TensorFlow と必要となる多数のライブラリをインポートします。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "jPo5bQwndr9P"
      },
      "outputs": [],
      "source": [
        "import tensorflow as tf\n",
        "\n",
        "import pandas as pd"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "c5w4m5gncnGh"
      },
      "source": [
        "## データセット\n",
        "\n",
        "このドキュメントのサンプルプログラムは、アヤメの花を、[萼片](https://en.wikipedia.org/wiki/Sepal)と[花弁](https://en.wikipedia.org/wiki/Petal)のサイズに基づいて、3 つの品種に分類するモデルを構築してテストします。\n",
        "\n",
        "モデルのトレーニングには、Iris データセットを使用します。Iris データセットには 4 つの特徴量と 1 つの[ラベル](https://developers.google.com/machine-learning/glossary/#label)が含まれます。4 つの特徴量は、次に示す各アヤメの植物学的特性を識別します。\n",
        "\n",
        "- 萼片の長さ\n",
        "- 萼片の幅\n",
        "- 花弁の長さ\n",
        "- 花弁の幅\n",
        "\n",
        "この情報に基づき、データを解析する上で役立ついくつかの定数を定義できます。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "lSyrXp_He_UE"
      },
      "outputs": [],
      "source": [
        "CSV_COLUMN_NAMES = ['SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth', 'Species']\n",
        "SPECIES = ['Setosa', 'Versicolor', 'Virginica']"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "j6mTfIQzfC9w"
      },
      "source": [
        "次に、Keras と Pandas を使用して、Iris データセットをダウンロードして解析します。トレーニング用とテスト用に別々のデータセットを維持することに注意してください。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "PumyCN8VdGGc"
      },
      "outputs": [],
      "source": [
        "train_path = tf.keras.utils.get_file(\n",
        "    \"iris_training.csv\", \"https://storage.googleapis.com/download.tensorflow.org/data/iris_training.csv\")\n",
        "test_path = tf.keras.utils.get_file(\n",
        "    \"iris_test.csv\", \"https://storage.googleapis.com/download.tensorflow.org/data/iris_test.csv\")\n",
        "\n",
        "train = pd.read_csv(train_path, names=CSV_COLUMN_NAMES, header=0)\n",
        "test = pd.read_csv(test_path, names=CSV_COLUMN_NAMES, header=0)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wHFxNLszhQjz"
      },
      "source": [
        "データを検査し、4 つの浮動小数型の特徴量カラムと 1 つの int32 ラベルがあることを確認します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "WOJt-ML4hAwI"
      },
      "outputs": [],
      "source": [
        "train.head()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "jQJEYfVvfznP"
      },
      "source": [
        "各データセットに対し、モデルが予測するようにトレーニングされるラベルを分割します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "zM0wz2TueuA6"
      },
      "outputs": [],
      "source": [
        "train_y = train.pop('Species')\n",
        "test_y = test.pop('Species')\n",
        "\n",
        "# The label column has now been removed from the features.\n",
        "train.head()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "jZx1L_1Vcmxv"
      },
      "source": [
        "## Estimator を使ったプログラミングの概要\n",
        "\n",
        "データのセットアップが完了したので、TensorFlow Estimator を使ってモデルを定義できます。Estimator は、`tf.estimator.Estimator` から派生したクラスです。TensorFlow は、一群の `tf.estimator`（`LinearRegressor` など）を提供しており、一般的な ML アルゴリズムを実装することができます。このほか、独自の[カスタム Estimator](https://www.tensorflow.org/guide/custom_estimators) を作成することもできますが、使用し始めには、事前作成済みの Estimator を使用することをお勧めします。\n",
        "\n",
        "事前作成済みの Estimator に基づいて TensorFlow プログラムを記述するには、次のタスクを実行する必要があります。\n",
        "\n",
        "- 1 つ以上の入力関数を作成する。\n",
        "- モデルの特徴量カラムを定義する。\n",
        "- Estimator をインスタンス化する。特徴量カラムとさまざまなハイパーパラメータを指定します。\n",
        "- Estimator オブジェクトに 1 つ以上のメソッドを呼び出す。データのソースとして適切な入力関数を渡します。\n",
        "\n",
        "では、アヤメの分類において、これらのタスクをどのように実装するのか見てみましょう。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "2OcguDfBcmmg"
      },
      "source": [
        "## 入力関数を作成する\n",
        "\n",
        "トレーニング、評価、および予測を行うためのデータを提供する入力関数を作成する必要があります。\n",
        "\n",
        "**入力関数**とは、次の要素タプルを出力する `tf.data.Dataset` オブジェクトを返す関数です。\n",
        "\n",
        "- [`features`](https://developers.google.com/machine-learning/glossary/#feature) - 次のような Python ディクショナリ。\n",
        "    - 各キーが特徴量の名前である。\n",
        "    - 各値が、特徴量の値のすべてを含む配列である。\n",
        "- `label` - 各サンプルの [label](https://developers.google.com/machine-learning/glossary/#label) の値を含む配列。\n",
        "\n",
        "入力関数の書式を示すために、単純な実装を次に示します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "nzr5vRr5caGF"
      },
      "outputs": [],
      "source": [
        "def input_evaluation_set():\n",
        "    features = {'SepalLength': np.array([6.4, 5.0]),\n",
        "                'SepalWidth':  np.array([2.8, 2.3]),\n",
        "                'PetalLength': np.array([5.6, 3.3]),\n",
        "                'PetalWidth':  np.array([2.2, 1.0])}\n",
        "    labels = np.array([2, 1])\n",
        "    return features, labels"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NpXvGjfnjHgY"
      },
      "source": [
        "入力関数を自分で作成すれば、`features` ディクショナリと `label` リストを好みに合わせて生成できるようにすることができますが、あらゆる種類のデータを解析できる TensorFlow の [Dataset API](https://www.tensorflow.org/guide/datasets) を使用することをお勧めします。\n",
        "\n",
        "Dataset API は、多数の一般的な事例を処理することができます。たとえば、Dataset API を使用すると、大量のファイルのレコードを並列して読み取り、単一のストリームに結合することが簡単に行えます。\n",
        "\n",
        "この例では事を単純にするために、[pandas](https://pandas.pydata.org/) でデータを読み込み、このメモリ内のデータから入力パイプラインを構築します。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "T20u1anCi8NP"
      },
      "outputs": [],
      "source": [
        "def input_fn(features, labels, training=True, batch_size=256):\n",
        "    \"\"\"An input function for training or evaluating\"\"\"\n",
        "    # Convert the inputs to a Dataset.\n",
        "    dataset = tf.data.Dataset.from_tensor_slices((dict(features), labels))\n",
        "\n",
        "    # Shuffle and repeat if you are in training mode.\n",
        "    if training:\n",
        "        dataset = dataset.shuffle(1000).repeat()\n",
        "    \n",
        "    return dataset.batch(batch_size)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xIwcFT4MlZEi"
      },
      "source": [
        "## 特徴量カラムを定義する\n",
        "\n",
        "[**特徴量カラム**](https://developers.google.com/machine-learning/glossary/#feature_columns)は、特徴量ディクショナリの生の入力データを、モデルがどのように使用すべきかを説明するオブジェクトです。Estimator モデルを作成する際に、モデルが使用する各特徴量を説明する特徴量カラムをモデルに渡します。`tf.feature_column` モジュールには、モデルに対してデータを表現するためのオプションが多数含まれています。\n",
        "\n",
        "Iris については、4 つの生の特徴量は数値であるため、Estimator に対して、これら 4 つの各特徴量を 32 ビットの浮動小数点数型の値として表現するように命令する特徴量カラムを構築します。したがって、特徴カラムを作成するためのコードは、次のようになります。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ZTTriO8FlSML"
      },
      "outputs": [],
      "source": [
        "# Feature columns describe how to use the input.\n",
        "my_feature_columns = []\n",
        "for key in train.keys():\n",
        "    my_feature_columns.append(tf.feature_column.numeric_column(key=key))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "jpKkhMoZljco"
      },
      "source": [
        "特徴量カラムは、ここに示すものよりもはるかに高度なものに構築することができます。特徴量カラムの詳細については、[こちらのガイド](https://www.tensorflow.org/guide/feature_columns)をご覧ください。\n",
        "\n",
        "モデルが生の特徴量をどのように表現するかに関する記述を準備できたので、Estimator を構築することができます。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "kuE59XHEl22K"
      },
      "source": [
        "## Estimator をインスタンス化する\n",
        "\n",
        "アヤメの問題はよく知られた分類問題です。幸いにも、TensorFlow は、次のような事前作成済みの分類子 Estimator を複数用意しています。\n",
        "\n",
        "- `tf.estimator.DNNClassifier`: 多クラス分類を実行するディープモデルに使用。\n",
        "- `tf.estimator.DNNLinearCombinedClassifier`: ワイド＆ディープモデルに使用。\n",
        "- `tf.estimator.LinearClassifier`: 線形モデルに基づく分類子に使用。\n",
        "\n",
        "アヤメの問題に関しては、`tf.estimator.DNNClassifier` が最適な選択肢と言えます。この Estimator をインスタンス化する方法を次に示します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "qnf4o2V5lcPn"
      },
      "outputs": [],
      "source": [
        "# Build a DNN with 2 hidden layers with 30 and 10 hidden nodes each.\n",
        "classifier = tf.estimator.DNNClassifier(\n",
        "    feature_columns=my_feature_columns,\n",
        "    # Two hidden layers of 30 and 10 nodes respectively.\n",
        "    hidden_units=[30, 10],\n",
        "    # The model must choose between 3 classes.\n",
        "    n_classes=3)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "tzzt5nUpmEe3"
      },
      "source": [
        "## トレーニングして評価して予測する\n",
        "\n",
        "Estimator オブジェクトを準備したので、次の項目を行うメソッド呼び出すことができます。\n",
        "\n",
        "- モデルをトレーニングする。\n",
        "- トレーニングされたモデルを評価する。\n",
        "- トレーニングされたモデルを使用して、予測を立てる。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "rnihuLdWmE75"
      },
      "source": [
        "### モデルをトレーニングする\n",
        "\n",
        "次のように、Estimator の `train` メソッドを呼び出して、モデルをトレーニングします。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "4jW08YtPl1iS"
      },
      "outputs": [],
      "source": [
        "# Train the Model.\n",
        "classifier.train(\n",
        "    input_fn=lambda: input_fn(train, train_y, training=True),\n",
        "    steps=5000)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ybiTFDmlmes8"
      },
      "source": [
        "Estimator が期待するとおり、引数を取らない入力関数を指定しながら、`input_fn` 呼び出しを [`lambda`](https://docs.python.org/3/tutorial/controlflow.html) にラッピングして引数をキャプチャするところに注意してください。`steps` 引数はメソッドに対して、あるトレーニングステップ数を完了した後にトレーニングを停止するように指定しています。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "HNvJLH8hmsdf"
      },
      "source": [
        "### トレーニングされたモデルを評価する\n",
        "\n",
        "モデルのトレーニングが完了したので、そのパフォーマンスに関する統計を得ることができます。次のコードブロックは、テストデータに対してトレーニングされたモデルの精度を評価します。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "A169XuO4mKxF"
      },
      "outputs": [],
      "source": [
        "eval_result = classifier.evaluate(\n",
        "    input_fn=lambda: input_fn(test, test_y, training=False))\n",
        "\n",
        "print('\\nTest set accuracy: {accuracy:0.3f}\\n'.format(**eval_result))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "VnPMP5EHph17"
      },
      "source": [
        "`train` メソッドへの呼び出しとは異なり、評価する`steps` 引数を渡していません。eval の `input_fn` データの単一の[エポック](https://developers.google.com/machine-learning/glossary/#epoch)のみを返します。\n",
        "\n",
        "`eval_result` ディクショナリには、`average_loss`（サンプル当たりの平均損失）、`loss`（ミニバッチ当たりの平均損失）、および Estimator の `global_step` の値（実行したトレーニングイテレーションの回数）も含まれます。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ur624ibpp52X"
      },
      "source": [
        "### トレーニングされたモデルから予測（推論）を立てる\n",
        "\n",
        "良質の評価結果を生み出すトレーニング済みのモデルを準備できました。これから、このトレーニング済みのモデルを使用し、ラベル付けできない測定に基づいてアヤメの品種を予測します。トレーニングと評価と同様に、単一の関数呼び出して予測を行います。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "wltc0jpgng38"
      },
      "outputs": [],
      "source": [
        "# Generate predictions from the model\n",
        "expected = ['Setosa', 'Versicolor', 'Virginica']\n",
        "predict_x = {\n",
        "    'SepalLength': [5.1, 5.9, 6.9],\n",
        "    'SepalWidth': [3.3, 3.0, 3.1],\n",
        "    'PetalLength': [1.7, 4.2, 5.4],\n",
        "    'PetalWidth': [0.5, 1.5, 2.1],\n",
        "}\n",
        "\n",
        "def input_fn(features, batch_size=256):\n",
        "    \"\"\"An input function for prediction.\"\"\"\n",
        "    # Convert the inputs to a Dataset without labels.\n",
        "    return tf.data.Dataset.from_tensor_slices(dict(features)).batch(batch_size)\n",
        "\n",
        "predictions = classifier.predict(\n",
        "    input_fn=lambda: input_fn(predict_x))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "JsETKQo0rHvi"
      },
      "source": [
        "`predict` メソッドは Python イテラブルを返し、各サンプルの予測結果のディクショナリを生成します。次のコードを使って、予測とその確率を出力します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Efm4mLzkrCxp"
      },
      "outputs": [],
      "source": [
        "for pred_dict, expec in zip(predictions, expected):\n",
        "    class_id = pred_dict['class_ids'][0]\n",
        "    probability = pred_dict['probabilities'][class_id]\n",
        "\n",
        "    print('Prediction is \"{}\" ({:.1f}%), expected \"{}\"'.format(\n",
        "        SPECIES[class_id], 100 * probability, expec))"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "premade.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
